﻿// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Roslynator.CSharp.Refactorings;

internal static class AddBracesRefactoring
{
    public static void ComputeRefactoring(RefactoringContext context, StatementSyntax statement)
    {
        if (context.IsAnyRefactoringEnabled(
            RefactoringDescriptors.AddBraces,
            RefactoringDescriptors.AddBracesToIfElse)
            && CanRefactor(context, statement))
        {
            if (context.IsRefactoringEnabled(RefactoringDescriptors.AddBraces))
            {
                RegisterRefactoring(context, statement);
            }

            if (context.IsRefactoringEnabled(RefactoringDescriptors.AddBracesToIfElse))
            {
                IfStatementSyntax topmostIf = GetTopmostIf(statement);

                if (topmostIf?.Else is not null
                    && GetEmbeddedStatements(topmostIf).Any(f => f != statement))
                {
                    context.RegisterRefactoring(
                        "Add braces to if-else",
                        ct => AddBracesToIfElseRefactoring.RefactorAsync(context.Document, topmostIf, ct),
                        RefactoringDescriptors.AddBracesToIfElse);
                }
            }
        }
    }

    private static IEnumerable<StatementSyntax> GetEmbeddedStatements(IfStatementSyntax topmostIf)
    {
        foreach (IfStatementOrElseClause ifOrElse in topmostIf.AsCascade())
        {
            StatementSyntax statement = ifOrElse.Statement;

            if (statement?.IsKind(SyntaxKind.Block) == false)
                yield return statement;
        }
    }

    public static void RegisterRefactoring(RefactoringContext context, StatementSyntax statement)
    {
        context.RegisterRefactoring(
            "Add braces",
            ct => RefactorAsync(context.Document, statement, ct),
            RefactoringDescriptors.AddBraces);
    }

    private static bool CanRefactor(RefactoringContext context, StatementSyntax statement)
    {
        return context.Span.IsEmptyAndContainedInSpanOrBetweenSpans(statement)
            && statement.IsEmbedded(canBeIfInsideElse: false);
    }

    private static IfStatementSyntax GetTopmostIf(StatementSyntax statement)
    {
        SyntaxNode parent = statement.Parent;

        if (parent is not null)
        {
            if (parent.IsKind(SyntaxKind.ElseClause))
            {
                return ((ElseClauseSyntax)parent).GetTopmostIf();
            }
            else if (parent is IfStatementSyntax parentStatement)
            {
                return parentStatement.GetTopmostIf();
            }
        }

        return null;
    }

    public static Task<Document> RefactorAsync(
        Document document,
        StatementSyntax statement,
        CancellationToken cancellationToken = default)
    {
        BlockSyntax block = SyntaxFactory.Block(statement)
            .WithFormatterAnnotation();

        return document.ReplaceNodeAsync(statement, block, cancellationToken);
    }
}
